package com.beidouapp.aspect;

import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.alibaba.fastjson.JSON;
import com.beidouapp.common.util.DateUtils;
import com.beidouapp.domain.annotation.Audit;
import com.beidouapp.domain.pojo.AuditLog;
import com.beidouapp.exception.BusinessException;
import com.beidouapp.security.model.ReturnDataInfo;
import com.beidouapp.security.model.SessionUser;
import com.beidouapp.security.utils.DscpCommUtils;
import com.beidouapp.service.AuditLogService;
import com.beidouapp.utils.UIUtils;

@Aspect
@Component
public class AuditLogAspect
{
	// 注入Service用于把日志保存数据库
	@Autowired
	private AuditLogService auditLogService;

	private static final String TIME = "time";
	
	/**用户ID**/
	private static final String USERID = "userid";
	
	/**用户姓名**/
	private static final String USERNAME = "username";

	/**
	 * 返回通知 用于拦截记录用户的操作
	 * @param jp 切点
	 * @param audit
	 * @return
	 */
	@AfterReturning(value="execution (* com.beidouapp.airport.mvc.*.*(..)) && @annotation(audit)", returning="returnVal")
	public void afterReturning(JoinPoint jp, Audit audit, Object returnVal)
	{
		try
		{
			HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
					.getRequest();
			AuditLog auditlogModel = new AuditLog();

			// 处理设置注解上的参数
			getMethodDescription(audit, jp, auditlogModel, request);

			// 设置日志描述模板key/value
			setMessageTemplate(audit, jp, returnVal, auditlogModel);
//			auditlogModel.setOrgCode(UIUtils.getSessionUser().getOrgCode());
			auditlogModel.setOrgCode("");
			auditLogService.insert(auditlogModel);
			
			final Logger logger = getLog(jp);
			logger.debug(auditlogModel.toString());
		}
		catch (Throwable e)
		{
			e.printStackTrace();
		}
	}

	// 标注该方法体为异常通知，当目标方法出现异常时，执行该方法体
	@AfterThrowing(pointcut = "within(com.beidouapp..mvc.*) && @annotation(audit)", throwing = "ex")
	public void addAfterThrowing(final JoinPoint jp, Audit audit, Exception ex)
	{
		final Logger logger = getLog(jp);
		if (ex != null)
		{
			logger.error(ex.getLocalizedMessage(), ex);
		}
	}

	/**
	 * 获取注解中对方法的描述信息 
	 * @param audit 注解
	 * @param joinPoint 切点
	 * @param auditlogModel 日志实体
	 * @param request
	 * @throws Exception
	 */
	private void getMethodDescription(Audit audit, JoinPoint joinPoint, AuditLog auditlogModel, HttpServletRequest request) throws Exception
	{
//		final Logger logger = getLog(joinPoint);
		// 设置action动作
		auditlogModel.setAction(audit.action());
		// 设置channel
		auditlogModel.setChannel(audit.channel().toString());

		SessionUser sessionUser = DscpCommUtils.getSessionUser(request.getSession());
		if(sessionUser != null){
			auditlogModel.setUserId(sessionUser.getLoginname());
			auditlogModel.setUserName(sessionUser.getUserName());
		}
		
		// 请求的IP
		String ip = getIpAddress(request);
		auditlogModel.setClientIp(ip);
		auditlogModel.setCreateDate(new Date());
		// 是否需要保存request，参数和值
//		if (audit.isSaveRequestData())
//		{
//			String arguments = "";
//			final String methodName = joinPoint.getSignature().getName();
//			final Object[] pargs = joinPoint.getArgs();
//			if (pargs == null || pargs.length == 0)
//			{
//				logger.debug("Entering method [" + methodName + " with arguments [" + arguments + "]");
//			}
//			else
//			{
//				try
//				{
//			        StringBuffer sb = new StringBuffer(); 
//					for (Object object : pargs)
//					{
//						sb.append(JSON.toJSONString(object)).append(",");
//					}
//					arguments = sb.toString();
//				}
//				catch (Exception e)
//				{
//					logger.error("Entering method [{} with arguments [{}]", methodName, Arrays.deepToString(pargs));
//				}
//			}
//			// 获取参数的信息
//			auditlogModel.setRequestParam(arguments);
//		}
	}


	/**
	 * 设置日志描述模板key/value
	 * @param audit
	 * @param jp
	 * @param returnVal
	 * @param auditlogModel
	 * @throws Throwable
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	private void setMessageTemplate(Audit audit, JoinPoint jp, Object returnVal, AuditLog auditlogModel) throws Throwable
	{
		try
		{
			Map<String, Object> messageTemplate = new HashMap<String, Object>();
			String[] msgKeys = null;
			String[] msgVals = null;
			ReturnDataInfo rTmp = null;
			auditlogModel.setResultStatus(1);
			msgKeys = audit.successParamKeys();
			msgVals = audit.successParams();
			if (returnVal != null && returnVal instanceof ReturnDataInfo)
			{
				rTmp = (ReturnDataInfo) returnVal;
				if (!rTmp.isFlag())
				{
					auditlogModel.setResultStatus(0);
					msgKeys = audit.errorParamKeys();
					msgVals = audit.errorParams();
				}
			}
			if(msgKeys.length != msgVals.length){
				throw new BusinessException("日志模板参数配置不正确！");
			}

			Object[] pargs = jp.getArgs();
			// 获取参数类型
			Class[] args = new Class[pargs.length];
			String longTemp = jp.getStaticPart().toLongString();
			String[] sArgs = (longTemp.substring(longTemp.lastIndexOf("(") + 1, longTemp.length() - 2)).split(",");
			for (int i = 0; i < args.length; i++)
			{
				if (sArgs[i].endsWith("String[]"))
				{
					args[i] = Array.newInstance(Class.forName("java.lang.String"), 1).getClass();
				}
				else if (sArgs[i].endsWith("Long[]"))
				{
					args[i] = Array.newInstance(Class.forName("java.lang.Long"), 1).getClass();
				}
				else if (sArgs[i].indexOf(".") == -1)
				{
					if (sArgs[i].equals("int"))
					{
						args[i] = int.class;
					}
					else if (sArgs[i].equals("char"))
					{
						args[i] = char.class;
					}
					else if (sArgs[i].equals("float"))
					{
						args[i] = float.class;
					}
					else if (sArgs[i].equals("long"))
					{
						args[i] = long.class;
					}
				}
				else
				{
					args[i] = Class.forName(sArgs[i]);
				}
			}

			
			Object val = "";
			int tmp = 0;//请求参数顺序编号
			for (int i = 0; i < msgKeys.length; i++)
			{
				// 如果是参数
				if (msgVals[i].startsWith("arg"))
				{
					tmp = Integer.valueOf(msgVals[i].substring(msgVals[i].indexOf("[") + 1, msgVals[i].indexOf("]")));
					if (msgVals[i].indexOf("$") == -1)
					{// 直接取参数值
						val = pargs[tmp];
					}
					else
					{// 取参数对象中某个属性
						if(msgVals[i].indexOf("mget") > 0)//从map中取值
						{
							Map mapTmp = (Map)pargs[tmp];
							val = mapTmp.get(msgVals[i].substring(msgVals[i].indexOf("(") + 1, msgVals[i].indexOf(")")));
						}
						else
						{
							Method m = args[tmp].getMethod(msgVals[i].substring(msgVals[i].indexOf("$") + 1));
							val = m.invoke(pargs[tmp]);
						}
					}
				}
				// 如果是返回值
				else if (msgVals[i].startsWith("returnVal"))
				{
					if (msgVals[i].indexOf("$") == -1)
					{// 直接取参数值
						val = returnVal == null ? "" : returnVal;
					}
					else
					{// 取参数对象中某个属性
						Method m = null;
						if (returnVal != null && returnVal instanceof ReturnDataInfo)
						{
							if(msgVals[i].contains("getMessage")){
								val = rTmp.getMessage();
							}
							else if (rTmp.getResult() != null)
							{
								m = rTmp.getResult().getClass().getMethod(msgVals[i].substring(msgVals[i].indexOf("$") + 1));
								val = String.valueOf(m.invoke(rTmp.getResult()));
							}
							else if (rTmp.getResults() != null)
							{// 数组情况
								val = "";
							}
							else
							{
								val = "";
							}
						}
						else
						{
							m = returnVal.getClass().getMethod(msgVals[i].substring(msgVals[i].indexOf("$") + 1));
							val = m.invoke(pargs[tmp]);
						}
					}
				}
				
				messageTemplate.put(msgKeys[i], val);
			}
			// 默认日志属性
			messageTemplate.put(TIME, DateUtils.getNow());
			messageTemplate.put(USERID, auditlogModel.getUserId());
			messageTemplate.put(USERNAME, auditlogModel.getUserName());
			
			auditlogModel.setMessageTemplate(JSON.toJSONString(messageTemplate));
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	}


	private Logger getLog(final JoinPoint joinPoint)
	{
		final Object target = joinPoint.getTarget();

		if (target != null)
		{
			return LoggerFactory.getLogger(target.getClass());
		}

		return LoggerFactory.getLogger(getClass());
	}

	/**
	 * 获取clientIp
	 * @param request
	 * @return
	 */
	public String getIpAddress(HttpServletRequest request) {
		String ip = request.getHeader("x-forwarded-for");
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("Proxy-Client-IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("WL-Proxy-Client-IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("HTTP_CLIENT_IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("HTTP_X_FORWARDED_FOR");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getRemoteAddr();
		}
		return ip;
	}
}
